import 'dart:convert';
import 'dart:io';

import '../leak/model/leak_info.dart';
import '../utils/log_util.dart';

final LocalSavaLeak localSavaLeak = LocalSavaLeak();

class LocalSavaLeak {
  LocalSavaLeak._internal();

  factory LocalSavaLeak() => _instance;

  static late final LocalSavaLeak _instance = LocalSavaLeak._internal();

  String _savePath = "";

  set savePath(String savePath) {
    _savePath = savePath;
  }

  // the queue to process.
  List<LeakInfo> _toProcessLeakList = [];

  Future<bool> insert(LeakInfo leakInfo) async {
    if (_savePath.isEmpty) {
      LogUtil.d("insert fail: savePath isEmpty.");
      return false;
    }
    if (null == leakInfo.node) return false;
    _enqueue(leakInfo);
    return true;
  }

  bool _isProcessing = false;

  Future<void> _processLeak() async {
    if (_savePath.isEmpty) {
      LogUtil.d("insert fail: savePath isEmpty.");
      return;
    }
    if (_isProcessing) return;
    _isProcessing = true;

    for (int i = 0; i < _toProcessLeakList.length; i++) {
      final leakInfo = _toProcessLeakList[i];
      if (null == leakInfo.node) return;
      try {
        File file = File(_savePath);
        if (!file.existsSync()) {
          file.createSync(recursive: true);
        }
        String jsonStr = await file.readAsString();
        if (jsonStr.isEmpty) {
          LeakList leakList = LeakList();
          leakList.leakInfo?.add(leakInfo);
          String jsonStr = json.encode(leakList.toJson());
          await file.writeAsString(jsonStr);
        } else {
          LeakList leakList = LeakList.fromJson(json.decode(jsonStr));
          if (true == leakList.leakInfo?.isNotEmpty) {
            int index =
                leakList.leakInfo?.indexWhere((element) => element.equal2another(leakInfo)) ?? -1;
            if (-1 != index) {
              leakList.leakInfo?[index].occurCount =
                  ((leakList.leakInfo?[index].occurCount) ?? 1) + 1;
              if (true == leakInfo.time?.isNotEmpty) {
                leakList.leakInfo?[index].time = leakInfo.time;
              } else {
                leakList.leakInfo?[index].time = DateTime.now().toString().substring(0, 19);
              }
            } else {
              leakList.leakInfo?.add(leakInfo);
            }
            String jsonStr = json.encode(leakList.toJson());
            await file.writeAsString(jsonStr);
          } else {
            LeakList leakList = LeakList();
            leakList.leakInfo?.add(leakInfo);
            String jsonStr = json.encode(leakList.toJson());
            await file.writeAsString(jsonStr);
          }
        }
      } catch (e) {
        LogUtil.d(e.toString());
      }
    }
    _toProcessLeakList = [];
    _isProcessing = false;
  }

  Future<void> _enqueue(LeakInfo leakInfo) async {
    await _spinAndWait();
    _toProcessLeakList.add(leakInfo);
    _processLeak();
  }

  Future<void> _spinAndWait() async {
    if (_isProcessing) {
      await Future.delayed(const Duration(milliseconds: 100));
      _spinAndWait();
    }
  }

  Future<void> printAllLeak() async {
    if (_savePath.isEmpty) {
      LogUtil.d("all leaks print: _savePath.isEmpty");
      return;
    }
    File file = File(_savePath);
    if (!file.existsSync()) {
      file.createSync(recursive: true);
    }
    String jsonStr = await file.readAsString();
    LogUtil.d("printAllLeak: " + jsonStr);
  }

  Future<void> clearSavedFile() async {
    if (_savePath.isEmpty) {
      LogUtil.d("clearSavedFile: _savePath.isEmpty");
      return;
    }
    File file = File(_savePath);
    if (file.existsSync()) {
      await file.delete();
    }
  }

  Future<LeakList?> getAllLeaks() async {
    if (_savePath.isEmpty) {
      LogUtil.d("getAllLeaks print: _savePath.isEmpty");
      return null;
    }
    File file = File(_savePath);
    if (!file.existsSync()) {
      LogUtil.d("getAllLeaks print: file not exists");
      return null;
    }
    String jsonStr = await file.readAsString();
    if (jsonStr.isEmpty) {
      LogUtil.d("getAllLeaks print: jsonStr is empty");
      return null;
    }
    LeakList leakList = LeakList.fromJson(json.decode(jsonStr));
    return leakList;
  }
}
